#! /bin/sh

# Kenton's Code Playground -- http://code.google.com/p/kentons-code
# Author: Kenton Varda (temporal@gmail.com)
# Copyright (c) 2010 Google, Inc. and contributors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

set -eu

if test $# = 0; then
  # Ekam is querying the script.  Tell it that we like C++ source files.
  echo trigger filetype:.cpp
  echo trigger filetype:.cc
  echo trigger filetype:.C
  echo trigger filetype:.cxx
  echo trigger filetype:.c++
  exit 0
fi

INPUT=$1

case "$INPUT" in
  *.cpp )
    MODULE_NAME=${INPUT%.cpp}
    ;;
  *.cc )
    MODULE_NAME=${INPUT%.cc}
    ;;
  *.C )
    MODULE_NAME=${INPUT%.C}
    ;;
  *.cxx )
    MODULE_NAME=${INPUT%.cxx}
    ;;
  *.c++ )
    MODULE_NAME=${INPUT%.c++}
    ;;
  * )
    echo "Wrong file type: $INPUT" >&2
    exit 1
    ;;
esac

echo findProvider special:ekam-interceptor
read INTERCEPTOR

if test "$INTERCEPTOR" = ""; then
  echo "error:  couldn't find intercept.so." >&2
  exit 1
fi

# Ask Ekam where to put the output file.  Actually, the compiler will make the same request again
# when it runs, but we need to know the location too.
OUTPUT=${MODULE_NAME}.o
echo newOutput "$OUTPUT"
read OUTPUT_DISK_PATH

CXX=${CXX:-c++}
CXXFLAGS=${CXXFLAGS:-}

# Remove -Wglobal-constructors in tests because the test framework depends on registering global
# objects, and we only care about global constructors in runtime code anyway.
case "$CXXFLAGS" in
  *-Wglobal-constructors* )
    case "$MODULE_NAME" in
      *-test )
        CXXFLAGS="$CXXFLAGS -Wno-global-constructors"
        ;;
    esac
esac

# Compile!  We LD_PRELOAD intercept.so to intercept open() and other filesystem calls and convert
# them into Ekam requests.  intercept.so expects file descriptors 3 and 4 to be the Ekam request
# and response streams, so we remap them to stdout and stdin, respectively.  We also remap stdout
# itself to stderr just to make sure that if the compiler prints anything to stdout (which normally
# it shouldn't), that does not get misinterpreted as an Ekam request.
#
# The DYLD_ vars are the Mac OSX equivalent of LD_PRELOAD.  We don't bother checking which OS we're
# on since the other vars will just be ignored anyway.
LD_PRELOAD=$INTERCEPTOR DYLD_FORCE_FLAT_NAMESPACE= DYLD_INSERT_LIBRARIES=$INTERCEPTOR \
    $CXX $CXXFLAGS -I/ekam-provider/c++header -c "/ekam-provider/canonical/$INPUT" \
    -o "${MODULE_NAME}.o" 3>&1 4<&0 >&2

# Ask Ekam where to put the symbol and deps lists.
echo newOutput "${MODULE_NAME}.o.syms"
read SYMFILE
echo newOutput "${MODULE_NAME}.o.deps"
read DEPFILE

# Generate the symbol list.
# TODO:  Would be nice to use nm -C here to demangle names but it doesn't appear
#   to be supported on OSX.
nm "$OUTPUT_DISK_PATH" > $SYMFILE

# Function which reads the symbol list on stdin and writes all symbols matching
# the given type pattern to stdout, optionally with a prefix.
readsyms() {
  grep '[^ ]*  *['$1'] ' | sed -e 's,^[^ ]*  *. \(.*\)$,'"${2:-}"'\1,g'
}

# Construct the deps file by listing all undefined symbols.
readsyms U < $SYMFILE > $DEPFILE

# Tell Ekam about the symbols provided by this file.
readsyms ABCDGRSTV "provide $OUTPUT_DISK_PATH c++symbol:" < $SYMFILE

# ========================================================================================
# Detect gtest-based tests and test support while we're here.
# TODO(kenton):  Probably should be a separate rule.

case $OUTPUT in
  */gtest_main.o )
    echo provide "$OUTPUT_DISK_PATH" gtest:main
    ;;
  */kj/test.o | kj/test.o )
    echo provide "$OUTPUT_DISK_PATH" kjtest:main
    ;;
  *_test.o | *_unittest.o | *_regtest.o | *-test.o )
    # Is this a gtest test that needs to link against gtest_main?
    if grep -q 7testing8internal23MakeAndRegisterTestInfo $DEPFILE && \
       ! egrep -q '[^U] _?main$' $SYMFILE; then
      echo provide "$OUTPUT_DISK_PATH" gtest:test
    fi

    # Is this a KJ test that needs to link against kj/test.o?
    if grep -q N2kj8TestCaseC $DEPFILE; then
      echo provide "$OUTPUT_DISK_PATH" kjtest:test
    fi
    ;;
  * )
    if egrep -q ' D [a-z0-9]+_module' $SYMFILE && \
       grep -q _ZN2v811HandleScopeC1Ev $DEPFILE; then
            echo provide "$OUTPUT_DISK_PATH" nodejs:module
    fi
    ;;
esac
